from datetime import datetime

from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
from odoo.tools import float_is_zero, float_compare, float_round

import logging

_logger = logging.getLogger(__name__)


class WorkorderDoneLineQty(models.TransientModel):
    _name = 'workorder.done.line.qty'
    _description = 'Workorder Done Line Qty'

    workorder_id = fields.Many2one('mrp.workorder', 'Work Order', required=True, ondelete='cascade')

    is_set_default = fields.Boolean(string="set default", default=False)

    qty_produced = fields.Integer(string='Produced')
    qty_reject = fields.Integer(string='Scrap')
    qty_produced_pnl = fields.Integer(string='Produced (PNL)')
    qty_reject_pnl = fields.Integer(string='Scrap (PNL)')
    qty_produced_set = fields.Integer(string='Produced (SET)')
    qty_reject_set = fields.Integer(string='Scrap (SET)')

    qty_wip = fields.Integer('Work In Process (WIP)', compute='_compute_qty_floor')
    qty_wip_pnl = fields.Integer('Work In Process (PNL)', compute='_compute_qty_floor')
    qty_wip_set = fields.Integer('Work In Process (SET)', compute='_compute_qty_floor')
    value_pnl = fields.Integer(string='PCS/PANEL', compute='_compute_qty_floor')
    value_set = fields.Integer(string='PCS/SET', compute='_compute_qty_floor')

    scrap_line_ids = fields.One2many('workorder.done.line.qty.reject.reason', 'line_id', 'Scrap Reason Allocation', copy=True)
    is_date_code = fields.Boolean(default=False, compute='_compute_is_date_code')
    date_code = fields.Char(string="DATE CODE")

    order_qty = fields.Integer('Order qty')
    order_set_qty = fields.Integer('Order SET qty')
    order_pnl_qty = fields.Integer('Order PNL qty')

    date_code_ids = fields.One2many('s.workorder.done.line.date.code', 'order_id', string='Date Code Ids')

    @api.depends('workorder_id')
    def _compute_is_date_code(self):
        workcenter_id = self.workorder_id.workcenter_id
        self.is_date_code = workcenter_id.is_date_code

    @api.depends('workorder_id')
    def _compute_qty_floor(self):
        for record in self:
            wo = record.workorder_id
            production = wo.production_id
            record.qty_wip = wo.qty_wip
            record.qty_wip_set = wo.qty_wip_set
            record.qty_wip_pnl = wo.qty_wip_pnl
            record.value_pnl = wo.value_pnl
            record.value_set = wo.value_set
            record.order_qty = production.product_uom_qty
            record.order_set_qty = production.product_uom_qty / wo.value_set
            record.order_pnl_qty = production.panel_uom_qty
            if not record.is_set_default:
                record.qty_produced_pnl = wo.qty_wip_pnl
                record.qty_produced_set = wo.qty_wip_set
                record.qty_produced = wo.qty_wip
                record.is_set_default = True

    @api.onchange('qty_produced_pnl')
    def _onchange_qty_produced_pnl(self):
        for i in self:
            wo = i.workorder_id
            value_ps = wo.value_pnl / wo.value_set
            i.qty_produced_set = i.qty_produced_pnl * value_ps
            i.qty_produced = i.qty_produced_pnl * wo.value_pnl

    @api.onchange('qty_reject_pnl')
    def _onchange_qty_reject_pnl(self):
        wo = self.workorder_id
        value_ps = wo.value_pnl / wo.value_set
        self.qty_reject_set = self.qty_reject_pnl * value_ps
        self.qty_reject = self.qty_reject_pnl * wo.value_pnl

        self.qty_produced_pnl = self.qty_wip_pnl - self.qty_reject_pnl
        self.qty_produced_set = self.qty_wip_set - self.qty_reject_set
        self.qty_produced = self.qty_wip - self.qty_reject

        precision = self.env['decimal.precision'].precision_get('Product Unit of Measure')
        if float_compare(self.qty_wip_pnl, self.qty_reject_pnl, precision_digits=precision) < 0:
            return {
                'warning': {
                    'title': 'warning',
                    'message': _('The number of scrapped products is greater than the number of work in progress')
                }
            }

    @api.onchange('qty_produced_set')
    def _onchange_qty_reject_set(self):
        for i in self:
            wo = i.workorder_id
            i.qty_produced = i.qty_produced_set * wo.value_set

    @api.onchange('qty_reject_set')
    def _onchange_qty_reject_set(self):
        for i in self:
            wo = i.workorder_id
            i.qty_reject = i.qty_reject_set * wo.value_set

    def move_by_step_auto(self, wo):
        if wo:
            prep_wos = wo._get_previous_work()
            if not prep_wos or prep_wos.state != 'done':
                return
            vals = {
                'qty_produced': prep_wos.qty_produced or wo.qty_producing or wo.qty_production,
                'state': 'done',
                'date_start': datetime.now(),
                'date_finished': datetime.now(),
                'costs_hour': wo.workcenter_id.costs_hour,
                'done_act_qty': prep_wos.done_act_qty
            }
            self.env['mrp.workorder.line'].create({
                    'workorder_id': wo.id,
                    'qty_produced_pnl': prep_wos.qty_op_produced_pnl,
                    'qty_produced_set': prep_wos.qty_op_produced_set,
                    'qty_produced': prep_wos.qty_op_produced,
                    'qty_reject_pnl': 0,
                    'qty_reject_set': 0,
                    'qty_reject': 0,
            })
            wo.end_all()
            wo.with_context(bypass_duration_calculation=True).write(vals)

    def _check_date_code(self, date_code):
        self.ensure_one()
        if len(date_code) != 4:
            raise ValidationError(_('The cycle code must be four digits'))
        if not date_code.isdigit():
            raise ValidationError(_('The cycle code must be all digits.'))
        # week = int(date_code[:2])
        # year = int(date_code[2:])
        # date_year = datetime.now().year % 100
        # if year > date_year:
        #     raise ValidationError(_('The year cannot be greater than %s', date_year))

    def create_production_product_lot(self, date_code):
        self.ensure_one()
        production_id = self.workorder_id.production_id
        product_id = production_id.product_id
        category_id = production_id.product_id.categ_id

        if category_id and category_id.code == 'CHP':
            lot_name = f'{product_id.name}-{production_id.base_mo}-{str(production_id.seq_no).zfill(2)}-{date_code}'
        else:
            lot_name = production_id.name
        lot_id = self.env['stock.lot'].search([('name', '=', lot_name), ('product_id', '=', product_id.id)], limit=1)
        if not lot_id:
            lot_id = self.env['stock.lot'].sudo().create({
                'name': lot_name,
                'product_id': product_id.id,
                's_week_of_year': date_code,
                's_production_id': production_id.id
            })
        return lot_id

    def done_move_qty(self):
        self.ensure_one()

        workorder = self.workorder_id
        production_id = self.workorder_id.production_id
        qty_wip_pnl = workorder.qty_wip_pnl
        qty_wip_set = workorder.qty_wip_set
        qty_wip = workorder.qty_wip

        prod = self.qty_produced
        prod_set = self.qty_produced_set
        prod_pnl = self.qty_produced_pnl
        rounding = workorder.product_uom_id.rounding

        # -1 过数不能大于WIP, 0 过数必须和wip相等
        if production_id.mo_type != 'W':
            if float_compare(qty_wip_pnl, prod_pnl, precision_rounding=rounding) == -1:
                raise ValidationError(_("The total processed quantity (produced + scrapped) %s exceeds the backlog.", prod_pnl))
            if float_compare(qty_wip_pnl, prod_pnl, precision_rounding=rounding) != 0:
                raise ValidationError(_('Partial overage is not allowed! %d--%d', qty_wip_pnl, prod_pnl))

            if float_compare(qty_wip_set, prod_set, precision_rounding=rounding) == -1:
                raise ValidationError(_("The total processed quantity (produced + scrapped) %s exceeds the backlog.", prod_set))
            if float_compare(qty_wip_set, prod_set, precision_rounding=rounding) != 0:
                raise ValidationError(_('Partial overage is not allowed! %d--%d', qty_wip_set, prod_set))

        if float_compare(qty_wip, prod, precision_rounding=rounding) == -1:
            raise ValidationError(_("The total processed quantity (produced + scrapped) %s exceeds the backlog.", prod))

        if float_compare(qty_wip, prod, precision_rounding=rounding) != 0:
            raise ValidationError(_('Partial overage is not allowed! %d--%d', qty_wip, prod))

        date_finished = datetime.now()
        self.env['mrp.workorder.line'].create({
            'workorder_id': workorder.id,
            'qty_produced_pnl': prod_pnl,
            'qty_produced_set': prod_set,
            'qty_produced': prod,
        })
        if self.is_date_code:
            # 库存位置
            stock_location_id = self.env['stock.location'].sudo().search([('barcode', '=', 'WH-STOCK-FINISHED-PRODUCT')], limit=1)
            if not self.date_code_ids:
                raise UserError(_('Please enter the Date Code!'))
            total = sum(self.date_code_ids.mapped('qty'))
            # uom_diff_total = float_round(production_id.product_qty - total, precision_rounding=rounding)
            # uom_total = float_round(total, precision_rounding=rounding)
            if total != self.qty_produced:
                raise UserError(_('Date Code qty Must be equal to the production quantity!'))

            date_code_qty = sum(self.date_code_ids.mapped('qty'))

            finished_move_ids = production_id.move_finished_ids
            finished_move = finished_move_ids[0]
            finished_move.move_line_ids.unlink()

            new_finished_move = finished_move.copy({
                'product_uom_qty': date_code_qty,
                'location_dest_id': stock_location_id.id,
            })
            move_line_ids = self.env['stock.move.line']

            for index, line in enumerate(self.date_code_ids.sorted(lambda x: x.qty, reverse=True)):
                self._check_date_code(line.date_code)
                lot_id = self.create_production_product_lot(line.date_code)
                if index == 0:
                    production_id.date_code = self.date_code
                    production_id.lot_producing_id = lot_id.id

                # if len(self.date_code_ids) > 1:
                move_line_ids |= self.env['stock.move.line'].create({
                    'move_id': new_finished_move.id,
                    'product_id': finished_move.product_id.id,
                    'product_uom_id': finished_move.product_uom.id,
                    'quantity': float_round(line.qty, precision_rounding=rounding),
                    'location_id': finished_move.location_id.id,
                    'location_dest_id': stock_location_id.id,
                    'company_id': finished_move.company_id.id or self.env.company.id,
                    'lot_id': lot_id.id,
                    'package_id': False,
                    'result_package_id': False,
                    'owner_id': False,
                    'picked': True
                })
            if move_line_ids:
                new_finished_move._action_done()
                move_line_ids.mapped('lot_id').write({'production_date': datetime.now()})

            # # 生产 到 生产前，报废
            scrap_finished_move_ids = production_id.move_finished_ids.filtered(lambda x: x.state not in ('done', 'cancel'))
            # 生产前位置
            location_id = self.env['stock.location'].sudo().search([('barcode', '=', 'WH-PREPRODUCTION')], limit=1)
            scrap_finished_move_ids.write({'location_dest_id': location_id.id})

        workorder.end_all()
        vals = {
            'qty_produced': workorder.qty_production,
            'state': 'done',
            'date_finished': date_finished,
            'costs_hour': workorder.workcenter_id.costs_hour,
            'done_act_qty': prod
        }
        if not workorder.date_start or date_finished < workorder.date_start:
            vals['date_start'] = date_finished
        workorder.with_context(bypass_duration_calculation=True, skip_mo_check=True).write(vals)
        next_wos = self.env['mrp.workorder'].search([('production_id', '=', production_id.id), ('step', '>', workorder.step)], order='step')
        for nwo in next_wos:
            # 不是计算点的自动过数
            if all([not nwo.workcenter_id.is_enter_bcode, not nwo.is_subcontracting]):
                self.move_by_step_auto(nwo)
            else:
                break

        if workorder.state == 'done':
            if workorder.is_last_unfinished_wo and not self._context.get('auto_unfinish', False):
                production_id.with_context(skip_consumption=True, bypass_duration_calculation=True, skip_expired=True).sudo().button_mark_done()
                production_id.is_autocomplete = True
                # 更新批次
                workorder._update_batch()
            # 完成物料消耗
            workorder.consumable_materials()
            # 完成半成品消耗
            workorder.consumable_materials_bcp()


class WorkorderDoneLineQty(models.TransientModel):
    _name = 'workorder.done.line.qty.reject.reason'
    _description = 'Workorder Done Line Qty of Reject Information'

    line_id = fields.Many2one('workorder.done.line.qty', "Counting Data")
    production_id = fields.Many2one('mrp.production', "Manufacturing Order", store=False)
    rep_workorder_id = fields.Many2one('mrp.workorder', 'Responsible Work Order',
                                       domain="[('production_id', '=', production_id)]")
    scrapreason_ids = fields.Many2many('scrap.reason', store=False)
    scrapreason_id = fields.Many2one('scrap.reason', string="Scrap Reason",
                                     ondelete='restrict', required=True, index=True,
                                     domain="[('id', 'in', scrapreason_ids)]")
    qty_reject = fields.Float(string='Scrap Quantity')

    @api.onchange('line_id')
    def _onchange_line_id(self):
        self.production_id = self.line_id.workorder_id.production_id

    @api.onchange('rep_workorder_id')
    def _onchange_rep_workorder_id(self):
        if self.rep_workorder_id:
            self.scrapreason_ids = self.rep_workorder_id.workcenter_id.scrapreason_ids
        else:
            self.scrapreason_ids = self.env['scrap.reason'].sudo()


class WorkorderDoneLineDateCode(models.TransientModel):
    _name = 's.workorder.done.line.date.code'
    _description = 'Workorder Done Line Date Code'

    order_id = fields.Many2one('workorder.done.line.qty')
    date_code = fields.Char(string='Date Code')
    qty = fields.Integer(string='Qty')
